package com.edar.dao.impl;

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.util.Iterator;
import java.util.List;

import org.hibernate.Criteria;
import org.hibernate.Hibernate;
import org.hibernate.HibernateException;
import org.hibernate.LockMode;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.Example;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.orm.hibernate3.HibernateCallback;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;

import com.edar.dao.GenericDao;
import com.edar.dao.util.QueryCondition;
/**
 * GenericDAO接口的Hibernate实现，完成通用的CRUD操作。
 * 来自：http://www.hibernate.org/328.html
 * 用spring进行改造
 * @author sys53
 */
public class GenericHibernateDao<T, ID extends Serializable> extends HibernateDaoSupport implements GenericDao<T, ID> {
	private static final Logger log = LoggerFactory.getLogger(GenericHibernateDao.class);
	
	private Class<T> persistentClass;
	
	@SuppressWarnings("unchecked")
	public GenericHibernateDao() {
			this.persistentClass = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass())
			.getActualTypeArguments()[0];
	}


	public Class<T> getPersistentClass() {
		return persistentClass;
	}
	@SuppressWarnings("unchecked")
	public List<T> findByExample(T exampleInstance,int offset,int maxResults) {
		return getHibernateTemplate().findByExample(persistentClass.getName(), Example.create(exampleInstance),offset,maxResults);
	}

	public T findById(ID id) {
		return findById(id,false);
	}
	@SuppressWarnings("unchecked")
	public T findById(ID id, boolean lock) {
		T entity;
		if (lock)
			entity = (T) getHibernateTemplate().load(getPersistentClass(), id, LockMode.UPGRADE);
		else
			entity = (T) getHibernateTemplate().load(getPersistentClass(), id);

		return entity;
	}

	public List<T> findAll() {
		return findByCriteria();
	}

	@SuppressWarnings("unchecked")
	public List<T> findByExample(T exampleInstance, String[] excludeProperty) {
		Criteria crit = getSession().createCriteria(getPersistentClass());
		Example example = Example.create(exampleInstance);
		for (String exclude : excludeProperty) {
			example.excludeProperty(exclude);
		}
		crit.add(example);
		return crit.list();
	}

	public T makePersistent(T entity) {
		getHibernateTemplate().saveOrUpdate(entity);
		return entity;
	}
	
	protected T persistent(T entity){
		getHibernateTemplate().persist(entity);
		return entity;
	}

	public void makeTransient(T entity) {
		getHibernateTemplate().delete(entity);
	}
	
	protected T merge(T entity){
		getHibernateTemplate().merge(entity);
		return entity;
	}

	protected void flush() {
		getHibernateTemplate().flush();
	}

	public void clear() {
		getHibernateTemplate().clear();
	}

	/**
	* Use this inside subclasses as a convenience method.
	*/
	@SuppressWarnings("unchecked")
	protected List<T> findByCriteria(Criterion... criterion) {
		Criteria crit = getSession().createCriteria(getPersistentClass());
		for (Criterion c : criterion) {
			crit.add(c);
		}
		return crit.list();
	}

	public int bulkUpdate(final QueryCondition qc) {
		Integer updateCount = (Integer) getHibernateTemplate().executeWithNativeSession(new HibernateCallback() {
			public Object doInHibernate(Session session) throws HibernateException {
				Query queryObject = createQueryByQueryCondition(session,qc);
				return new Integer(queryObject.executeUpdate());
			}
		});
		return updateCount.intValue();
	}
	
	@SuppressWarnings("unchecked")
	public List<?> find(final QueryCondition qc){
		return (List<?>) getHibernateTemplate().executeWithNativeSession(new HibernateCallback() {
			public Object doInHibernate(Session session) throws HibernateException {
				Query queryObject = createQueryByQueryCondition(session,qc);
				return queryObject.list();
			}
		});
	}
	
	private void prepareQueryCondition(Query qry,QueryCondition qc){
		if(qc.getOffset()>0)
			qry.setFirstResult(qc.getOffset());
		log.debug("第一个结果位置：{}",qc.getOffset());
		if(qc.getMaxResults()>0)
			qry.setMaxResults(qc.getMaxResults());
		log.debug("最大结果数：{}",qc.getMaxResults());
	}
	/**
	 * 根据QueryCondition创建Query对象
	 * @param session
	 * @param qc
	 * @return
	 */
	private Query createQueryByQueryCondition(Session session,QueryCondition qc){
		Query qry = session.createQuery(qc.getQueryString());
		if(qc.isParamQuery()){
			log.debug("按名字参数创建Query");
			for(String param : qc.getParamNames()){
				log.debug("参数名：{},参数值：{}",param, qc.getParamValue(param));
				qry.setParameter(param, qc.getParamValue(param));
			}
		}else{
			log.debug("按?参数创建Query");
			for(int i=0; i<qc.getValues().size(); i++){
				log.debug("序号：{},参数值：{}",i, qc.getValues().get(i));
				qry.setParameter(i, qc.getValues().get(i));
			}
		}
		prepareQueryCondition(qry,qc);
		return qry;
	}
	
	@SuppressWarnings("unchecked")
	public Iterator<?> iterator(final QueryCondition qc){
		return (Iterator<?>) getHibernateTemplate().executeWithNativeSession(new HibernateCallback() {
			public Object doInHibernate(Session session) throws HibernateException {
				Query queryObject = createQueryByQueryCondition(session,qc);
				return queryObject.iterate();
			}
		});
	}
	@Autowired
	public void setSessionFactory0(SessionFactory sessionFactory){
		super.setSessionFactory(sessionFactory);
	}
	/**
	 * hibernate 延时加载内容
	 * @param proxy
	 */
	public void initialize(Object proxy){
		Hibernate.initialize(proxy);
	}
}